<script setup lang="ts">
import { computed, inject, onMounted, ref, watch } from 'vue'
import _ from 'lodash'
import { ControlBalls } from '@renderer/views/multiScanSeriesPage/helper/helper'
import { fabric } from 'fabric'

const props = defineProps(['seriesId', 'canvasId'])
const emits = defineEmits(['positionChange'])
const { scanSeriesArr } = inject('multiSeries') as any
const { activeOpTypeRef } = inject('viewOps') as any
const { isFaultScan } = inject('stackViewerInfo', { isFaultScan: false }) as any
const canvasRef = ref<any>(null)
const drawLine = computed(() => isFaultScan.value)
let circleList: any[] = []
let circleListDataHelper: any[] = []
let centerOpPoint: any

const currentSeries = computed(() => {
  const seriesIndex = scanSeriesArr.findIndex(item => item.id === props.seriesId)
  return scanSeriesArr[seriesIndex]
})


const ballsInfoRef = computed(() => {
  const ballsInfo = currentSeries.value.ballsInfo
  if (ballsInfo === undefined || ballsInfo.length === 0) {
    return {
      points: [],
      centerOp: {}
    }
  } else {
    return ballsInfo[props.canvasId]
  }
})


watch(activeOpTypeRef, (newVal: string) => {
  if (newVal === 'area') {
    ControlCanvasOp.enable()
  } else {
    ControlCanvasOp.disable()
  }
})

class ControlCanvasOp {
  static enable() {
    circleList.forEach(circle => {
      circle.set({
        hoverCursor: 'move',
        lockMovementX: false,   // 锁定X轴移动
        lockMovementY: false   // 锁定Y轴移动
      })
    })
    centerOpPoint.set({
      hoverCursor: 'move',
      lockMovementX: false,   // 锁定X轴移动
      lockMovementY: false   // 锁定Y轴移动
    })
    canvas.renderAll()

  }

  static disable() {
    circleList.forEach(circle => {
      circle.set({
        hoverCursor: 'default',// 鼠标悬停时的光标样式
        lockMovementX: true,   // 锁定X轴移动
        lockMovementY: true   // 锁定Y轴移动
      })
    })
    centerOpPoint.set({
      hoverCursor: 'default',// 鼠标悬停时的光标样式
      lockMovementX: true,   // 锁定X轴移动
      lockMovementY: true   // 锁定Y轴移动
    })
    canvas.renderAll()
  }
}


const movingHelper = {
  preTop: 0,
  preLeft: 0,
  deltaY: 0,
  deltaX: 0,  // 以上4个用来计算每次移动的差值。
  initTop: 0,
  initLeft: 0,//初始的top 和left 为了保持移动小球的时候一个坐标可以不变。
  preTop1: 0,
  preLeft1: 0, // 这个用来计算 拖拽MR ML 两个点。
  movingPoint: ''
}

const limit = {
  maxAngle: 120,
  minAngle: 60,
  start: 0,
  end: Number.MAX_SAFE_INTEGER
}

const movingPosiHelper = {
  angle: 90,
  start: 0,
  end: 0,
  height: 0,
  width: 0
}


let angle = 90
let horizonLines: any = []
let polygon: any
let canvas

let resizeObserver: ResizeObserver
const watchResize = () => {
  resizeObserver = new ResizeObserver(
    _.debounce((entries) => {
      const { width, height } = entries[0].contentRect
      if (width !== 0 && height !== 0) {
        init()
        resizeObserver.disconnect()
      }
    })
  )
  // 绑定监听的dom
  resizeObserver.observe(canvasRef.value)
}

onMounted(() => {
  watchResize()
})


const init = () => {
  const { width, height } = canvasRef.value.getBoundingClientRect()
  console.log(`draggable canvas width:${width},${height}`)
  canvasRef.value.width = width
  canvasRef.value.height = height
  canvas = new fabric.Canvas(canvasRef.value)

  const {
    points,
    centerOp
  } = ballsInfoRef.value

  canvas.selection = false

  // 根据点坐标生成多边形
  polygon = new fabric.Polygon(
    points, // 顶点坐标集
    {
      fill: 'transparent', // 填充红色
      stroke: 'white', // 边框黑色
      strokeWidth: 2, // 边框粗细
      objectCaching: false, // 当“true”时，对象缓存在另一个画布上。当“false”时，除非必要(clipPath)默认为 true，否则不缓存对象。默认是true
      selectable: false, // 禁止选中
      evented: false // 当设置为“false”时，对象不能成为事件的目标。所有事件都会通过它传播。
    }
  )

  const text = new fabric.Text(angle.toString(), {
    left: points[0].x + 10,
    top: points[0].y + 10,
    fontSize: 16,
    fontWeight: 'bold',
    selectable: false,
    fill: 'white'
  })

  //根据点坐标生成多边形的9个操作点。
  points.forEach((item, index) => {
    const options: any = {
      left: item.x,
      top: item.y,
      strokeWidth: 5,
      radius: 12,
      fill: '#fff',
      stroke: '#666',
      originX: 'center', // x轴方向以中心点为原点
      originY: 'center', // y轴方向以中心点为原点
      hasControls: false, // 不显示控制器
      hasBorders: false, // 不显示控制器的边
      cid: `circle-${index}`, // 自定义属性
      pType: item.pType,
      lockRotation: true
    }
    const circle = new fabric.Circle(options)
    circleList.push(circle)
    circleListDataHelper.push({
      left: item.x,
      top: item.y
    })
  })
  centerOpPoint = new fabric.Circle({
    left: centerOp.x,
    top: centerOp.y,
    strokeWidth: 5,
    radius: 12,
    fill: '#fff',
    stroke: '#666',
    originX: 'center', // x轴方向以中心点为原点
    originY: 'center', // y轴方向以中心点为原点
    hasControls: false, // 不显示控制器
    hasBorders: false, // 不显示控制器的边
    pType: ControlBalls.CENTER
  } as any)

  if (drawLine.value) {
    renderHorLines()
  }
  canvas.add(polygon, ...circleList, centerOpPoint, text)

  canvas.on('mouse:up', () => {
    if (drawLine.value) {
      const distance = circleList[5].top - circleList[1].top
      let deltaY = 0
      if (movingHelper.movingPoint === ControlBalls.TM) {
        const nearestPosi = circleList[5].top - Math.round(distance / 20) * 20
        deltaY = nearestPosi - circleList[1].top
        ;[0, 1, 2].forEach(index => {
          circleList[index].top = circleList[index].top + deltaY
          polygon.points[index].y = polygon.points[index].y + deltaY
        })
        ;[3, 7].forEach(index => {
          circleList[index].top = circleList[index].top + deltaY / 2
          polygon.points[index].y = polygon.points[index].y + deltaY / 2
        })
      } else if (movingHelper.movingPoint === ControlBalls.BM) {
        const nearestPosi = circleList[1].top + Math.round(distance / 20) * 20
        deltaY = nearestPosi - circleList[5].top
        ;[4, 5, 6].forEach(index => {
          circleList[index].top = circleList[index].top + deltaY
          polygon.points[index].y = polygon.points[index].y + deltaY
        })
        ;[3, 7].forEach(index => {
          circleList[index].top = circleList[index].top + deltaY / 2
          polygon.points[index].y = polygon.points[index].y + deltaY / 2
        })
      }
      syncCenterOpPosition()
      renderHorLines()
    }

    Object.assign(movingHelper, {
      preTop: 0,
      preLeft: 0,
      deltaY: 0,
      deltaX: 0,  // 以上4个用来计算每次移动的差值。
      initTop: 0,
      initLeft: 0,//初始的top 和left 为了保持移动小球的时候一个坐标可以不变。
      preTop1: 0,
      preLeft1: 0,
      movingPoint: ''
    })
  })


  // 监听画布上的元素移动事件
  canvas.on('object:moving', _.throttle(function(e) {
      // getCurrentBallsPosition(polygon.points)
      // 当前移动的元素
      let target: any = e.target
      let index = 0
      if (target.cid) {
        index = target.cid.split('-')[1]
      }

      if (movingHelper.preTop === 0) {
        movingHelper.preTop = target.top
      } else {
        movingHelper.deltaY = target.top - movingHelper.preTop
        movingHelper.preTop = target.top
      }

      if (movingHelper.preLeft === 0) {
        movingHelper.preLeft = target.left
      } else {
        movingHelper.deltaX = target.left - movingHelper.preLeft
        movingHelper.preLeft = target.left
      }

      if (movingHelper.initTop === 0) {
        movingHelper.initTop = target.top
      }
      if (movingHelper.initLeft === 0) {
        movingHelper.initLeft = target.left
      }
      const slope = (movingHelper.initTop - centerOpPoint.top) / (movingHelper.initLeft - centerOpPoint.left)
      const b = centerOpPoint.top - slope * centerOpPoint.left
      const halfHeight = Number.parseInt((Math.abs(circleList[6].top - circleList[0].top) / 2).toFixed(0))

      switch (target.pType) {
        // 移动左上顶点
        case ControlBalls.TL:
          target.left = movingHelper.initLeft
          if (LimitCheck.checkMoveFit(ControlBalls.TL)) {
            [2, 3, 4].forEach(index => {
              circleList[index].top = polygon.points[index].y - movingHelper.deltaY
              circleListDataHelper[index].top = circleList[index].top
              polygon.points[index].y = polygon.points[index].y - movingHelper.deltaY
            });
            [0, 6, 7].forEach(index => {
              circleList[index].top = polygon.points[index].y + movingHelper.deltaY
              polygon.points[index].y = polygon.points[index].y + movingHelper.deltaY
            })
          } else {
            target.top = polygon.points[index].y
          }
          break
        case ControlBalls.BL:
          target.left = movingHelper.initLeft
          if (LimitCheck.checkMoveFit(ControlBalls.BL)) {
            ;[2, 3, 4].forEach(index => {
              circleList[index].top = polygon.points[index].y - movingHelper.deltaY
              polygon.points[index].y = polygon.points[index].y - movingHelper.deltaY
            });
            [0, 6, 7].forEach(index => {
              circleList[index].top = polygon.points[index].y + movingHelper.deltaY
              polygon.points[index].y = polygon.points[index].y + movingHelper.deltaY
            })
          } else {
            target.top = polygon.points[index].y
          }
          break
        case ControlBalls.TR:
          target.left = movingHelper.initLeft
          if (LimitCheck.checkMoveFit(ControlBalls.TR)) {
            ;[2, 3, 4].forEach(index => {
              circleList[index].top = polygon.points[index].y + movingHelper.deltaY
              polygon.points[index].y = polygon.points[index].y + movingHelper.deltaY
            });
            [0, 6, 7].forEach(index => {
              circleList[index].top = polygon.points[index].y - movingHelper.deltaY
              polygon.points[index].y = polygon.points[index].y - movingHelper.deltaY
            })
          } else {
            target.top = polygon.points[index].y
          }
          break
        case ControlBalls.BR:
          target.left = movingHelper.initLeft
          if (LimitCheck.checkMoveFit(ControlBalls.BR)) {
            ;[2, 3, 4].forEach(index => {
              circleList[index].top = polygon.points[index].y + movingHelper.deltaY
              polygon.points[index].y = polygon.points[index].y + movingHelper.deltaY
            });
            [0, 6, 7].forEach(index => {
              circleList[index].top = polygon.points[index].y - movingHelper.deltaY
              polygon.points[index].y = polygon.points[index].y - movingHelper.deltaY
            })
          } else {
            target.top = polygon.points[index].y
          }
          break

        case ControlBalls.TM:
          movingHelper.movingPoint = ControlBalls.TM
          target.left = movingHelper.initLeft;
          [0, 1, 2].forEach(index => {
            circleList[index].top = polygon.points[index].y + movingHelper.deltaY
            polygon.points[index].y = polygon.points[index].y + movingHelper.deltaY
          });
          [3, 7].forEach(index => {
            circleList[index].top = polygon.points[index].y + movingHelper.deltaY / 2
            polygon.points[index].y = polygon.points[index].y + movingHelper.deltaY / 2
          })
          centerOpPoint.top = centerOpPoint.top + movingHelper.deltaY / 2
          break
        case ControlBalls.BM:
          movingHelper.movingPoint = ControlBalls.BM
          target.left = movingHelper.initLeft;
          [4, 5, 6].forEach(index => {
            circleList[index].top = polygon.points[index].y + movingHelper.deltaY
            polygon.points[index].y = polygon.points[index].y + movingHelper.deltaY
          });
          [3, 7].forEach(index => {
            circleList[index].top = polygon.points[index].y + movingHelper.deltaY / 2
            polygon.points[index].y = polygon.points[index].y + movingHelper.deltaY / 2
          })
          centerOpPoint.top = centerOpPoint.top + movingHelper.deltaY / 2
          break

        case ControlBalls.ML:
          target.top = slope * target.left + b
          if (movingHelper.preTop1 === 0) {
            movingHelper.preTop1 = target.top
          } else {
            movingHelper.deltaY = target.top - movingHelper.preTop1
            movingHelper.preTop1 = target.top
          }
          circleList[0].left = target.left
          circleList[0].top = target.top - halfHeight

          circleList[6].left = target.left
          circleList[6].top = target.top + halfHeight

          ;[0, 6, 7].forEach(index => {
          polygon.points[index].x = circleList[index].left
          polygon.points[index].y = circleList[index].top
        })
        ;[2, 3, 4].forEach(index => {
          circleList[index].left = circleList[index].left - movingHelper.deltaX
          circleList[index].top = circleList[index].top - movingHelper.deltaY
          polygon.points[index].x = circleList[index].left
          polygon.points[index].y = circleList[index].top
        })
          break

        case ControlBalls.MR:
          target.top = slope * target.left + b
          if (movingHelper.preTop1 === 0) {
            movingHelper.preTop1 = target.top
          } else {
            movingHelper.deltaY = target.top - movingHelper.preTop1
            movingHelper.preTop1 = target.top
          }
          circleList[2].left = target.left
          circleList[2].top = target.top - halfHeight

          circleList[4].left = target.left
          circleList[4].top = target.top + halfHeight


          ;[0, 6, 7].forEach(index => {
          circleList[index].left = circleList[index].left - movingHelper.deltaX
          circleList[index].top = circleList[index].top - movingHelper.deltaY
          polygon.points[index].x = circleList[index].left
          polygon.points[index].y = circleList[index].top
        })

        ;[2, 3, 4].forEach(index => {
          polygon.points[index].x = circleList[index].left
          polygon.points[index].y = circleList[index].top
        })
          break

        case ControlBalls.CENTER:
          ;[...circleList].forEach(eachPoint => {
          eachPoint.top = eachPoint.top + movingHelper.deltaY
          eachPoint.left = eachPoint.left + movingHelper.deltaX
        })
          horizonLines.forEach(line => {
            line.set({
              x1: line.x1 + movingHelper.deltaX,
              y1: line.y1 + movingHelper.deltaY,
              x2: line.x2 + movingHelper.deltaX,
              y2: line.y2 + movingHelper.deltaY
            })
          })
          polygon.points.forEach(eachPoint => {
            eachPoint.x = eachPoint.x + movingHelper.deltaX
            eachPoint.y = eachPoint.y + movingHelper.deltaY
          })
          break
        default:
          break
      }
      syncCenterOpPosition();
      // 更新操作点的坐标，以便能再次操作。
      [...circleList, centerOpPoint].forEach(eachPoint => {
        eachPoint.setCoords()
      })
      if (drawLine.value) {
        renderHorLines()
      }
      text.text = movingPosiHelper.angle.toString()
      text.left = circleList[0].left + 10
      text.top = circleList[0].top + 10
    }, 30)
  )

  ControlCanvasOp.enable()
}

const syncCenterOpPosition = () => {
  centerOpPoint.top = (circleList[3].top + circleList[7].top) / 2
  centerOpPoint.left = (circleList[3].left + circleList[7].left) / 2
}

const renderHorLines = () => {
  if (horizonLines) {
    horizonLines.forEach(line => {
      canvas.remove(line)
    })
    horizonLines = []
  }
  horizonLines = getHorLines(polygon)
  horizonLines.forEach(line => {
    canvas.add(line)
    canvas.sendToBack(line)
  })
}


const getHorLines = (polygon) => {
  const eachSplitHeight = 20
  let lines: any[] = []
  const counts = (polygon.points[6].y - polygon.points[0].y) / eachSplitHeight
  for (let i = 1; i < counts; i++) {
    const p0 = {
      x: polygon.points[0].x,
      y: polygon.points[0].y + eachSplitHeight * i
    }
    const p1 = {
      x: p0.x + polygon.points[2].x - polygon.points[0].x,
      y: polygon.points[2].y + eachSplitHeight * i
    }
    const line = new fabric.Line([p0.x, p0.y, p1.x, p1.y], {
      stroke: 'yellow',
      selectable: false
    })
    lines.push(line)
  }
  return lines
}

const emitPositionChange = _.debounce(() => {
  emits('positionChange', {
    angle: movingPosiHelper.angle
  })
}, 100)

class LimitCheck {
  static checkMoveFit = (type: ControlBalls) => {
    let angle = 90
    if (type === ControlBalls.TL) {
      angle = this.getThreePointsAngle(2, 0, 6)
    } else if (type === ControlBalls.BL) {
      angle = 180 - this.getThreePointsAngle(4, 6, 0)
    } else if (type === ControlBalls.TR) {
      angle = 180 - this.getThreePointsAngle(0, 2, 4)
    } else if (type === ControlBalls.BR) {
      angle = this.getThreePointsAngle(2, 4, 6)
    } else if (type === ControlBalls.ML) {
      angle = 180 - this.getThreePointsAngle(3, 7, 0)
    } else if (type === ControlBalls.MR) {
      angle = this.getThreePointsAngle(2, 3, 7)
    } else {
      angle = 90
    }
    if (angle >= limit.minAngle && angle <= limit.maxAngle) {
      movingPosiHelper.angle = Number.parseInt(angle.toFixed(0))
      emitPositionChange()
      return true
    }
    return false
  }

  private static getThreePointsAngle = (index0, centerIndex, index1) => {
    return this.getAngle({
        x: circleList[centerIndex].left - circleList[index1].left,
        y: circleList[centerIndex].top - circleList[index1].top
      }, {
        x: circleList[centerIndex].left - circleList[index0].left,
        y: circleList[centerIndex].top - circleList[index0].top
      }
    )
  }

  private static getAngle = (v1, v2) => {
    var dotProduct = v1.x * v2.x + v1.y * v2.y
    var magnitude1 = Math.sqrt(v1.x * v1.x + v1.y * v1.y)
    var magnitude2 = Math.sqrt(v2.x * v2.x + v2.y * v2.y)
    var cosTheta = dotProduct / (magnitude1 * magnitude2)
    var theta = Math.acos(cosTheta)
    return theta * (180 / Math.PI)
  }
}

const getCurrentBallsPosition = _.debounce((points: { x: number, y: number, pType: string }[]) => {
  Object.assign(currentSeries.value.ballsInfo[props.canvasId], {
    centerOp: {
      x: centerOpPoint.left,
      y: centerOpPoint.top
    },
    points: points
  })
},300)


</script>

<template>
  <canvas ref="canvasRef" class="w-full h-full" style="border:1px solid #ccc"></canvas>
</template>

<style scoped>

</style>
